﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using Storage.RemoteStorage.Sevice.Entity;
using System.IO;
using System.Configuration;
using Storage.RemoteStorage.Sevice.Entity.Transfert;
using System.Security.Cryptography;

namespace Storage.RemoteStorage.Sevice.Manager
{
    public class VirtualFileManager
    {
        public VirtualFolder RootFolder
        {
            get
            {
                return GetRootFolder();
            }
        }

        public VirtualFolder GetRootFolder()
        {
            VirtualFolder root = new VirtualFolder();
            root.IsRoot = true;
            root.Name = @"Root";
            root.RelativePath = @"\";
            root.ServerPath = ConfigurationManager.AppSettings["RootPath"];
            this.RefrecheCild(ref root);
            return root;
        }

        public int GetTotalSubFolder(ref VirtualFolder folder)
        {
            if (folder == null)
                folder = GetRootFolder();
            DirectoryInfo currentFolder = new DirectoryInfo(folder.ServerPath);
            return currentFolder.GetDirectories().Length;
        }

        public int GetTotalSubFiles(ref VirtualFolder folder)
        {
            if (folder == null)
                folder = GetRootFolder();
            DirectoryInfo currentFolder = new DirectoryInfo(folder.ServerPath);
            return currentFolder.GetFiles().Length;
        }

        public List<VirtualFile> GetPagedFiles(int PageSize, int PageIndex, VirtualFolder Parent, out bool EndofFile)
        {
            EndofFile = false;
            if (Parent == null)
                Parent = GetRootFolder();
            DirectoryInfo currentFolder = new DirectoryInfo(Parent.ServerPath);
            FileInfo[] subFolder = currentFolder.GetFiles();
            if ((PageSize * PageIndex) <= subFolder.Length)
                subFolder = subFolder.Skip((PageIndex) * PageSize).Take(PageSize).ToArray();
            else
                EndofFile = true;
            List<VirtualFile> Result = new List<VirtualFile>();
            foreach (FileInfo file in subFolder)
            {
                VirtualFile folder = new VirtualFile();
                folder.Parent = Parent;
                folder.Size = file.Length;
                folder.Name = file.Name;
                folder.RelativePath = Path.Combine(Parent.RelativePath, file.Name);
                folder.ServerPath = file.FullName;
                Result.Add(folder);
                //Parent.SubFolder.Add(folder);
                //Parent.Size += folder.Size;
            }
            return Result;
        }

        public List<VirtualFolder> GetPagedSubFolder(int PageSize, int PageIndex, VirtualFolder Parent, out bool EndOfFile)
        {
            EndOfFile = false;
            if (Parent == null)
                Parent = GetRootFolder();
            DirectoryInfo currentFolder = new DirectoryInfo(Parent.ServerPath);
            DirectoryInfo[] subFolder = currentFolder.GetDirectories();
            if ((PageSize * PageIndex) <= subFolder.Length)
                subFolder = subFolder.Skip((PageIndex) * PageSize).Take(PageSize).ToArray();
            else
                EndOfFile = true;
            List<VirtualFolder> Result = new List<VirtualFolder>();
            foreach (DirectoryInfo subfolder in subFolder)
            {
                VirtualFolder folder = new VirtualFolder();
                folder.Parent = Parent;
                folder.Size = 0;
                folder.Name = subfolder.Name;
                folder.RelativePath = Path.Combine(Parent.RelativePath, subfolder.Name);
                folder.ServerPath = subfolder.FullName;
                Result.Add(folder);
                //Parent.SubFolder.Add(folder);
                //Parent.Size += folder.Size;
            }
            return Result;
        }

        private void RefrecheCild(ref VirtualFolder root)
        {
            if (root == null)
                root = GetRootFolder();
            DirectoryInfo currentFolder = new DirectoryInfo(root.ServerPath);
            DirectoryInfo[] surbfolders = currentFolder.GetDirectories();
            FileInfo[] subfiles = currentFolder.GetFiles();

            root.nbFiles = subfiles.Length;
            root.nbSubFolder = surbfolders.Length;
            root.IsLoaded = true;
        }

        public VirtualFolder CreateFolder(string name, VirtualFolder Parent)
        {
            if (Parent == null)
                Parent = GetRootFolder();
            DirectoryInfo dir = new DirectoryInfo(Path.Combine(Parent.ServerPath, name));
            if (dir.Exists)
                throw new Exception("le Dossier existe déja");
            dir.Create();
            VirtualFolder result = new VirtualFolder()
            {
                Name = name,
                Parent = Parent,
                ServerPath = dir.FullName,
                IsRoot = false,
                IsLoaded = true,
                nbFiles = 0,
                nbSubFolder = 0,
                RelativePath = Path.Combine(Parent.RelativePath, name)
            };
            return result;
        }

        public VirtualFolder GetFolder(string Name, VirtualFolder Parent)
        {
            //RefrecheCild(ref Parent);
            if (Parent == null)
                Parent = GetRootFolder();
            DirectoryInfo dir = new DirectoryInfo(Parent.ServerPath);
            DirectoryInfo result = dir.GetDirectories().Where(k => k.Name.Equals(Name)).FirstOrDefault();
            if (result != null)
            {
                VirtualFolder resultData = new VirtualFolder()
                {
                    Parent = Parent,
                    nbFiles = result.GetFiles().Length,
                    IsLoaded = true,
                    IsRoot = false,
                    Name = result.Name,
                    RelativePath = Path.Combine(Parent.RelativePath, result.Name),
                    ServerPath = Path.Combine(Parent.ServerPath, result.Name),
                    nbSubFolder = result.GetDirectories().Length
                };
                return resultData;
            }
            else
                return null;
        }

        public VirtualFolder GetFolderbyPath(string folderPath, char PathSeparator, VirtualFolder Parent, bool createifNoExist)
        {
            //RefrecheCild(ref Parent);
            string[] Foldertree = folderPath.Split(PathSeparator);
            VirtualFolder result = Parent;
            foreach (string partialPath in Foldertree)
            {
                VirtualFolder oldResult = result;
                result = GetFolder(partialPath, result);
                if (result == null && createifNoExist)//le dossier n'existe pas
                    result = CreateFolder(partialPath, oldResult);
            }
            return result;
        }


        public VirtualFile GetFile(string Name, VirtualFolder Parent)
        {
            RefrecheCild(ref Parent);
            if (Parent.nbFiles > 0)
            {
                FileInfo file = new FileInfo(Path.Combine(Parent.ServerPath, Name));
                if (file.Exists)
                {
                    return new VirtualFile()
                    {
                        Name = Name,
                        Parent = Parent,
                        RelativePath = Path.Combine(Parent.RelativePath, Name),
                        ServerPath = file.FullName,
                        Size = file.Length
                    };
                }
                else
                    return null;
            }
            else
                return null;
        }

        public VirtualFile GetFile(string FilePath)
        {
            string FPath = string.Empty;
            if (FilePath.StartsWith("\\"))
                FPath = RootFolder.ServerPath + FilePath.Substring(1, FilePath.Length - 1);
            else
                FPath = Path.Combine(RootFolder.ServerPath, FilePath);
            FileInfo info = new FileInfo(FPath);
            if (info.Exists)
                return new VirtualFile()
                {
                    Name = info.Name,
                    Parent = null,
                    RelativePath = FilePath,
                    ServerPath = FPath,
                    Size = info.Length
                };
            else
                return null;
        }

        public RemoteFileInfo GetFileContent(VirtualFile file)
        {
            if (file != null)
            {
                try
                {
                    FileInfo f = new FileInfo(file.ServerPath);
                    RemoteFileInfo result = new RemoteFileInfo();
                    result.FileName = f.Name;
                    result.Length = f.Length;
                    result.FileByteStream = new System.IO.FileStream(f.FullName, FileMode.Open, FileAccess.Read);

                    return result;
                }
                catch (Exception ex)
                {
                    throw ex;
                }
            }
            else
                throw new Exception("Fichier introuvable");
        }

        public void CloseFileContent(VirtualFile file)
        {

        }

        public bool DeleteFile(string Name, VirtualFolder Parent)
        {
            VirtualFile file = GetFile(Name, Parent);
            if (file != null)
                return DeleteFile(file);
            else
                return false;
        }

        private bool DeleteFile(VirtualFile file)
        {
            if (file != null)
            {
                try
                {
                    FileInfo serverFile = new FileInfo(file.ServerPath);
                    if (serverFile.Exists)
                    {
                        serverFile.Delete();
                        return true;
                    }
                    else
                        throw new Exception("Fichier local Introuvable (Problem de synchro)");
                }
                catch (Exception ex)
                {
                    throw ex;
                }
            }
            else
                throw new Exception("Fichier distant introuvable");
        }

        public bool DeleteFolder(string Name, VirtualFolder Parent)
        {
            VirtualFolder Folder = null;
            if (Parent == null)
                Parent = GetRootFolder();

            Folder = GetFolder(Name, Parent);
            if (Folder != null)
                return DeleteFolder(Folder);
            else
                return false;
        }

        public bool DeleteFolder(VirtualFolder folder)
        {
            if (folder != null)
            {
                try
                {
                    DirectoryInfo serverFolder = new DirectoryInfo(folder.ServerPath);
                    if (serverFolder.Exists)
                    {
                        serverFolder.Delete(true);
                        return true;
                    }
                    else
                        throw new Exception("Dossier local Introuvable (Problem de synchro)");
                }
                catch (Exception ex)
                {
                    throw ex;
                }
            }
            else
                throw new Exception("Dossier distant introuvable");

        }

        public bool MoveFolder(string Name, VirtualFolder Parent, VirtualFolder NewParent)
        {
            if (Parent == null)
                Parent = GetRootFolder();
            else
                if (!Parent.IsLoaded)
                    RefrecheCild(ref Parent);
            if (Parent.nbSubFolder > 0)
            {
                VirtualFolder folder = GetFolder(Name, Parent);
                return MoveFolder(folder, NewParent);
            }
            else
                return true;
        }
        public bool MoveDirectory(string sourceDirName, string destDirName, bool overwrite)
        {
            if (overwrite && Directory.Exists(destDirName))
            {
                DirectoryInfo sourceFolder = new DirectoryInfo(sourceDirName);
                sourceFolder.Refresh();
                IEnumerable<FileInfo> files = sourceFolder.EnumerateFiles();
                IEnumerable<DirectoryInfo> SubFolder = sourceFolder.EnumerateDirectories();

                foreach (FileInfo file in files)
                {
                    file.CopyTo(Path.Combine(destDirName, file.Name), overwrite);
                }
                foreach (DirectoryInfo subfolder in SubFolder)
                {
                    MoveDirectory(subfolder.FullName, Path.Combine(destDirName, subfolder.Name), overwrite);
                }
                sourceFolder.Refresh();
                if (sourceFolder.Exists)
                    sourceFolder.Delete(true);
                return true;
            }
            else
            {
                Directory.Move(sourceDirName, destDirName); // Can throw an Exception
                return true;
            }
        }
        public bool MoveFolder(VirtualFolder folder, VirtualFolder NewParent)
        {
            if (folder != null)
            {
                try
                {
                    DirectoryInfo serverFolder = new DirectoryInfo(folder.ServerPath);
                    DirectoryInfo serverFolderDest = new DirectoryInfo(NewParent.ServerPath);
                    if (serverFolder.Exists)
                    {
                        if (serverFolderDest.Exists)
                        {
                            DirectoryInfo newFolder = new DirectoryInfo(Path.Combine(serverFolderDest.FullName, serverFolder.Name));
                            if (!newFolder.Exists)
                            {
                                serverFolder.MoveTo(newFolder.FullName);
                                return true;
                            }
                            else//Prevoire la comparaison des dossier si le dossier cible existe
                            {
                                MoveDirectory(serverFolder.FullName, newFolder.FullName, true);
                                return true;
                            }
                        }
                        else
                            throw new Exception("Dossier local destination invalide (Problem de synchro)");

                    }
                    else
                        throw new Exception("Dossier local Introuvable (Problem de synchro)");
                }
                catch (Exception ex)
                {
                    throw ex;
                }
            }
            else
                throw new Exception("Dossier distant introuvable");

        }

        private void DirectoryCopy(string sourceDirName, string destDirName, bool copySubDirs)
        {
            // Get the subdirectories for the specified directory.
            DirectoryInfo dir = new DirectoryInfo(sourceDirName);
            DirectoryInfo[] dirs = dir.GetDirectories();

            if (!dir.Exists)
            {
                throw new DirectoryNotFoundException(
                    "Source directory does not exist or could not be found: "
                    + sourceDirName);
            }

            // If the destination directory doesn't exist, create it. 
            if (!Directory.Exists(destDirName))
            {
                Directory.CreateDirectory(destDirName);
            }

            // Get the files in the directory and copy them to the new location.
            FileInfo[] files = dir.GetFiles();
            foreach (FileInfo file in files)
            {
                string temppath = Path.Combine(destDirName, file.Name);
                file.CopyTo(temppath, false);
            }

            // If copying subdirectories, copy them and their contents to new location. 
            if (copySubDirs)
            {
                foreach (DirectoryInfo subdir in dirs)
                {
                    string temppath = Path.Combine(destDirName, subdir.Name);
                    DirectoryCopy(subdir.FullName, temppath, copySubDirs);
                }
            }
        }

        public bool CopyFolder(string Name, VirtualFolder Parent, VirtualFolder NewParent)
        {
            if (Parent == null)
                Parent = GetRootFolder();
            else
                if (!Parent.IsLoaded)
                    RefrecheCild(ref Parent);

            VirtualFolder folder = GetFolder(Name, Parent);
            if (folder != null)
            {
                try
                {
                    DirectoryInfo serverFolder = new DirectoryInfo(folder.ServerPath);
                    DirectoryInfo serverFolderDest = new DirectoryInfo(NewParent.ServerPath);
                    if (serverFolder.Exists)
                    {
                        if (serverFolderDest.Exists)
                        {
                            this.DirectoryCopy(serverFolder.FullName, Path.Combine(serverFolderDest.FullName, serverFolder.Name), true);
                            return true;
                        }
                        else
                            throw new Exception("Dossier local destination invalide (Problem de synchro)");

                    }
                    else
                        throw new Exception("Dossier local Introuvable (Problem de synchro)");
                }
                catch (Exception ex)
                {
                    throw ex;
                }
            }
            else
                throw new Exception("Dossier distant introuvable");

        }

        public bool SaveFile(RemoteFileInfo RemoteFile)
        {
            string FPath = string.Empty;
            if (RemoteFile.FileName.StartsWith("\\"))
                FPath = RootFolder.ServerPath + RemoteFile.FileName.Substring(1, RemoteFile.FileName.Length - 1);
            else
                FPath = Path.Combine(RootFolder.ServerPath, RemoteFile.FileName);
            FileInfo newFile = new FileInfo(FPath);
            if (newFile.Exists)
                newFile.Delete();
            if (RemoteFile.FileByteStream != null)
            {
                int BufferSize = 60 * 1024;
                using (FileStream Writer = newFile.OpenWrite())
                {
                    RemoteFile.FileByteStream.CopyTo(Writer, BufferSize);
                    return true;
                }
            }
            else
                return false;
        }

        internal string GetHashCode(VirtualFile Name, VirtualFolder Parent)
        {
            RefrecheCild(ref Parent);
            VirtualFile file = this.GetFile(Name.Name, Parent);
            if (file != null)
            {
                using (var md5 = MD5.Create())
                {
                    using (var stream = File.OpenRead(file.ServerPath))
                    {
                        return Convert.ToBase64String(md5.ComputeHash(stream));
                    }
                }
            }
            return string.Empty;
        }

        internal bool RenameFolder(string OldName, string NewName, VirtualFolder Parent)
        {
            try
            {
                if (Parent == null)
                    Parent = GetRootFolder();
                RefrecheCild(ref Parent);
                VirtualFolder Folder = GetFolder(OldName, Parent);
                if (Folder != null)
                {
                    DirectoryInfo Dir = new DirectoryInfo(Folder.ServerPath);
                    if (Dir.Exists)
                    {
                        MoveDirectory(Folder.ServerPath, Path.Combine(Parent.ServerPath, NewName), true);
                        return true;
                    }
                    else
                        throw new System.IO.DirectoryNotFoundException("Dossier source Introuvable");
                }
                else
                    throw new System.IO.DirectoryNotFoundException("Dossier source (Virtual) Introuvable");
            }
            catch (Exception ex)
            {

                throw ex;
            }

        }

        internal bool RenameFile(string OldName, string NewName, VirtualFolder Parent)
        {
            if (Parent == null)
                Parent = GetRootFolder();
            VirtualFile file = GetFile(OldName, Parent);
            if (file != null)
            {
                FileInfo fileinfo = new FileInfo(file.ServerPath);
                if (fileinfo.Exists)
                {
                    fileinfo.MoveTo(Path.Combine(Parent.ServerPath, NewName));
                    RefrecheCild(ref Parent);
                    return true;
                }
                else
                    throw new System.IO.DirectoryNotFoundException("Dossier Introuvable");
            }
            else
                throw new System.IO.DirectoryNotFoundException("Dossier Introuvable");
        }

        internal VirtualFolder GetFolderbyIndex(int index, VirtualFolder Parent)
        {
            //RefrecheCild(ref Parent);
            if (Parent == null)
                Parent = GetRootFolder();
            DirectoryInfo inf = new DirectoryInfo(Parent.ServerPath);
            DirectoryInfo[] subfolders = inf.GetDirectories();

            if (subfolders.Length >= index)
            {
                DirectoryInfo dir = subfolders.ElementAt(index);
                return new VirtualFolder()
                {
                    IsLoaded = false,
                    IsRoot = false,
                    Name = dir.Name,
                    nbFiles = dir.GetFiles().Length,
                    Parent = Parent,
                    RelativePath = dir.FullName,
                    ServerPath = Path.Combine(Parent.ServerPath, dir.Name),
                    Size = 0,
                    nbSubFolder = dir.GetDirectories().Length
                };
            }
            else
                return null;
        }
    }
}